#include "Socket.h"
#include <unistd.h>

__attribute((constructor)) void wsaInit() {
    WSADATA wsa_data;
    WSAStartup(MAKEWORD(2, 2), &wsa_data);
}

__attribute((destructor)) void wsaClear() {
    WSACleanup();
}

Socket::Socket(const char *host, int port) {
    sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    addr.sin_family = AF_INET;
    addr.sin_addr.S_un.S_addr = inet_addr(host);
    addr.sin_port = htons(port);
}

Socket::Socket(SOCKET sock, sockaddr_in addr) :
        sock(sock), addr(addr) {}

Socket::~Socket() {
    close();
}

[[maybe_unused]] Socket::Socket(Socket &&other) noexcept {
    *this = std::move(other);
}

Socket &Socket::operator=(Socket &&other) noexcept {
    sock = other.sock;
    addr = other.addr;
    other.sock = INVALID_SOCKET;
    other.addr = {};
    return *this;
}

void Socket::close() {
    closesocket(sock);
    sock = INVALID_SOCKET;
    addr = {};
}

void Socket::send(const char *buf, int len) const {
    int cur = 0;
    while (cur != len) {
        int res = ::send(sock, buf + cur, len - cur, 0);
        if (res <= 0) {
            throw std::runtime_error("发送失败：" + std::to_string(res));
        }
        cur += res;
    }
}

void Socket::recv(char *buf, int len) const {
    int cur = 0;
    while (cur != len) {
        int res = ::recv(sock, buf + cur, len - cur, 0);
        if (res <= 0) {
            throw std::runtime_error("接收失败：" + std::to_string(res));
        }
        cur += res;
    }
}

Server::Server(const char *host, int port) :
        Socket(host, port) {}

void Server::listen(const std::function<void(Socket)> &handle) {
    if (bind(sock, (SOCKADDR *) &addr, sizeof(addr))) {
        close();
        throw std::runtime_error("服务端绑定失败");
    }

    ::listen(sock, SOMAXCONN);

    std::vector<std::thread> thread_list;
    while (true) {
        SOCKET client_socket;
        sockaddr_in client_addr{};
        int addr_len = sizeof(client_addr);
        client_socket = accept(sock, (SOCKADDR *) &client_addr, &addr_len);
        if (client_socket == INVALID_SOCKET) {
            break;
        }
        std::thread client_thread(handle, Socket(client_socket, client_addr));
        thread_list.push_back(std::move(client_thread));
    }
    for (auto &t: thread_list) {
        t.join();
    }
}

Client::Client(const char *host, int port) :
        Socket(host, port) {
    while (connect(sock, (SOCKADDR *) &addr, sizeof(addr))) {
        sleep(1);
    }
}
